#include "Ymodem.h"
#include "Hal_Delay.h"
#include "string.h"
#include "usart.h"
#include "boot_interface.h"
#include "utill.h"

#define UARTxPort UART_CH1

uint8_t aFileName[FILE_NAME_LENGTH];
__IO uint32_t flashdestination;
uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE];
//返回值 1成功，0失败
static uint8_t ReceivePacket(uint8_t *pData,uint32_t *pBufferLength,uint16_t timeout)
{
	uint32_t crc;	//校验码
	uint32_t packet_size = 0;		//包大小
	uint8_t status = 0;			//状态码
	uint8_t char1;
	
	*pBufferLength = 0;			//指针
	//上位机向下位机发送字符
	//status = uart1_receive(&char1, 1, timeout);		//接收1个字符 用于判断
	
	 if (status == 1)
	 {
		switch(char1)
		{
			case SOH:	//Ymodem 128byte modeml	工作在128K 模式下
				packet_size = PACKET_SIZE;
				break;
			
			case STX: //ymodem 1k 工作在1k 模式下
				packet_size = PACKET_1K_SIZE;
				break;
			
			case EOT:	// 接收到EOT（End of Transmission）	结束传输
				
			break;
			
			case CA:	// 接收到CA（Cancel）
				if((UARTx_Receive(UARTxPort,&char1, 1, timeout) == 1) && (char1 == CA))
				{
					packet_size = 2;
				}
				else{
					status = 0;
				}
				break;
			case ABORT1:	//A终止传输
			case ABORT2:	//a终止传输
				status = 0;
				break;
			default:		// 接收到其他字符
				status = 0;
				break;
			
		}
		*pData = char1; // 将接收到的字符存储到数据缓冲区	
		//判断SOH SOX 
		if(packet_size >= PACKET_SIZE)
		{
			//pData[PACKET_NUMBER_INDEX]  128/1024+头+尾 长度 由于start字符已经处理过了所以从number开始接收处理
			status = UARTx_Receive(UARTxPort,&pData[PACKET_NUMBER_INDEX], packet_size+PACKET_OVERHEAD_SIZE,timeout);
			
			if(status == 1)
			{
				//包编号与包编号补码一致跳过 不一致则丢弃数据包
				if(pData[PACKET_NUMBER_INDEX] !=((pData[PACKET_CNUMBER_INDEX])^ NEGATIVE_BYTE))
				{
					packet_size = 0;
					status = 0;
				}
				else
				{
					/* Check packet CRC */
					crc = pData[ packet_size + PACKET_DATA_INDEX ] << 8;//拿到CRC0
					crc += pData[ packet_size + PACKET_DATA_INDEX + 1 ];//拿到CRC1 组合成CRC
					//计算CRC 由于下标 0 1 2 3 4 第4开始才是数据所以从第四个开始
					if( Cal_CRC16(&pData[PACKET_DATA_INDEX],packet_size) != crc)	
					{
						packet_size = 0;
						status = 0;
					}
				}
			}
			else
			{
				packet_size = 0;
			}
		}
	 }
	 *pBufferLength = packet_size;
	 return status;
}

uint16_t Cal_CRC16(const uint8_t* p_data, uint32_t size)
{
  uint32_t crc = 0;
  const uint8_t* dataEnd = p_data+size;

  while(p_data < dataEnd)
    crc = UpdateCRC16(crc, *p_data++);
 
  crc = UpdateCRC16(crc, 0);
  crc = UpdateCRC16(crc, 0);

  return crc&0xffffu;
}

COM_StatusTypeDef Ymodem_Receive(uint32_t *p_size)
{
	uint32_t i,packet_length,session_done = 0,file_done,ErrorStatus = 0,session_begin = 0;
	uint32_t filesize, packets_received;		//文件大小 接收数据包
	uint8_t *file_ptr;		
	uint8_t file_size[FILE_SIZE_LENGTH], tmp;					
	COM_StatusTypeDef result = COM_OK;			//默认成功
	
	/* Initialize flashdestination variable */
	flashdestination = BLOCK_APP1_START;			//写入的FLASH起始地址
	
	while ((session_done == 0) && (result == COM_OK)) //传输未完成或者无错误则继续
	{
		packets_received = 0;	//数据包接收计数器
		file_done = 0;			//结束标志位
		while((file_done == 0) && (result == COM_OK))//传输未完成或者传输无异常
		{
			switch(ReceivePacket(aPacketData,&packet_length,DOWNLOAD_TIMEOUT))		//接收 1成功 0 失败
			{
				case 1:
					ErrorStatus = 0;	//错误状态为0 继续执行下一行程序
				switch(packet_length)
				{
					case 2:		//如果返回为2 用户放弃
						UARTx_SendByte(UARTxPort,ACK);
						result = COM_ABORT;
						break;			//立即跳出程序
					case 0:	//如果返回0 结束传输
						UARTx_SendByte(UARTxPort,ACK);
						file_done = 1;
						break;			//立即跳出程序
					default:		//都不是那么说明没有异常发生
						
					if(aPacketData[PACKET_CNUMBER_INDEX] !=(uint8_t)packets_received)	//packets_received:期望接收到的包号 结束后自增 aPacketData[PACKET_CNUMBER_INDEX]:提取接收到的一包的包号
						{
							 UARTx_SendByte(UARTxPort,NAK);
						}
						else
						{
							if(packets_received == 0)	//接收计数为0则是第一个数据包 包含文件名和大小
							{
								if(aPacketData[PACKET_DATA_INDEX] != 0)	//包含文件名包
								{
									i = 0;
									file_ptr = aPacketData + PACKET_DATA_INDEX;	//file_ptr 指针移动到文件名的下方
									 while ( (*file_ptr != 0) && (i < FILE_NAME_LENGTH))
									{
									  aFileName[i++] = *file_ptr++;
									}
									
									aFileName[i++] = '\0';	//为文件名收尾
									i = 0;			//i复位
									file_ptr ++;	//指针指向文件尾
									
									while ( (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH))
									{
									  file_size[i++] = *file_ptr++;
									}
									file_size[i++] = '\0';
									Str2Int(file_size, &filesize);
									
								/* Test the size of the image to be sent */
								/* Image size is greater than Flash size */
								if (*p_size > (BLCOK_APP1_SPACE + 1))
								{
								  /* End session */
									tmp = CA;
									UARTx_SendByte(UARTxPort,tmp);
									UARTx_SendByte(UARTxPort,tmp);
								  result = COM_LIMIT;
								}
						//擦除App Flash 区	
						//		bsp_flash_erase_block(BLOCK_APP1_START, BLOCK_APP1_NUM);	//需要实现
								*p_size = filesize;

								 UARTx_SendByte(UARTxPort,ACK);
								 UARTx_SendByte(UARTxPort,CRC16);
								}
								 /* File header packet is empty, end session */
								else
								{
									UARTx_SendByte(UARTxPort,ACK);
									file_done = 1;
									session_done = 1;
									break;
								}
							}
							else /* Data packet 写入数据到flash中*/	
							{
	//						  boot_interface_write_flash(flashdestination, &aPacketData[PACKET_DATA_INDEX], packet_length);
								
							  /* Write received data in Flash */
							  flashdestination += packet_length;	//保存写入指针
							  UARTx_SendByte(UARTxPort,ACK);
							}
							packets_received ++;
							session_begin = 1;
						}
						break;
					}
					break;
					default:
					if(session_begin > 0){
					
						ErrorStatus ++;
					}
					if (ErrorStatus > MAX_ERRORS){
					
						/* Abort communication */
						UARTx_SendByte(UARTxPort,CA);
						UARTx_SendByte(UARTxPort,CA);
					}
					else{
						
						UARTx_SendByte(UARTxPort,CRC16); /* Ask for a packet */
					}
				  break;
				
			}
		}
	}
	 return result;
}

/**
  * @brief  Update CRC16 for input byte
  * @param  crc_in input value 
  * @param  input byte
  * @retval None
  */
uint16_t UpdateCRC16(uint16_t crc_in, uint8_t byte)
{
  uint32_t crc = crc_in;
  uint32_t in = byte | 0x100;

  do
  {
    crc <<= 1;
    in <<= 1;
    if(in & 0x100)
      ++crc;
    if(crc & 0x10000)
      crc ^= 0x1021;
  }
  
  while(!(in & 0x10000));

  return crc & 0xffffu;
}

/**
  * @brief  Cal CRC16 for YModem Packet
  * @param  data
  * @param  length
  * @retval None
  */


/**
  * @brief  Calculate Check sum for YModem Packet 使用累加校验方法
  * @param  p_data Pointer to input data
  * @param  size length of input data
  * @retval uint8_t checksum value
  */
uint8_t CalcChecksum(const uint8_t *p_data, uint32_t size)
{
  uint32_t sum = 0;
  const uint8_t *p_data_end = p_data + size;

  while (p_data < p_data_end )
  {
    sum += *p_data++;
  }

  return (sum & 0xffu);
}

